/*
* heatmap.js Google Maps Overlay
*
* Copyright (c) 2008-2016, Patrick Wied (https://www.patrick-wied.at)
* Dual-licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
* and the Beerware (http://en.wikipedia.org/wiki/Beerware) license.
*/
;(function (name, context, factory) {
  // Supports UMD. AMD, CommonJS/Node.js and browser context
  if (typeof module !== "undefined" && module.exports) {
    module.exports = factory(
      require('heatmap.js'),
      require('google-maps')
    );
  } else if (typeof define === "function" && define.amd) {
    define(['heatmap.js', 'google-maps'], factory);
  } else {
    // browser globals
    if (typeof window.h337 === 'undefined') {
      throw new Error('heatmap.js must be loaded before the gmaps heatmap plugin');
    }
    if (typeof window.google === 'undefined') {
      throw new Error('Google Maps must be loaded before the gmaps heatmap plugin');
    }
    context[name] = factory(window.h337, window.google.maps);
  }

})("HeatmapOverlay", this, function(h337, gmaps) {
  'use strict';

  var HeatmapOverlay = function(map, cfg){ 
    this.setMap(map);
    this.initialize(cfg || {});
  };

  HeatmapOverlay.prototype = new gmaps.OverlayView();

  HeatmapOverlay.CSS_TRANSFORM = (function() {
    var div = document.createElement('div');
    var props = [
      'transform',
      'WebkitTransform',
      'MozTransform',
      'OTransform',
      'msTransform'
    ];

    for (var i = 0; i < props.length; i++) {
      var prop = props[i];
      if (div.style[prop] !== undefined) {
        return prop;
      }
    }

    return props[0];
  })();

  HeatmapOverlay.prototype.initialize = function(cfg) {
    this.cfg = cfg;
    
    var map = this.map = this.getMap();
    var container = this.container = document.createElement('div');
    var mapDiv = map.getDiv();
    var width = this.width = mapDiv.clientWidth;
    var height = this.height = mapDiv.clientHeight;

    container.style.cssText = 'width:' + width +'px;height:' + height+'px;';

    this.data = [];
    this.max = 1;
    this.min = 0;

    cfg.container = container;
  };

  HeatmapOverlay.prototype.onAdd = function(){
    var that = this;

    this.getPanes().overlayLayer.appendChild(this.container);


    this.changeHandler = gmaps.event.addListener(
      this.map,
      'bounds_changed',
      function() { return that.draw(); }
    );
   
    if (!this.heatmap) {
      this.heatmap = h337.create(this.cfg);
    }
    this.draw();
  };

  HeatmapOverlay.prototype.onRemove = function() { 
    if (!this.map) { return; }

    this.map = null;

    this.container.parentElement.removeChild(this.container);

    if (this.changeHandler) {
      gmaps.event.removeListener(this.changeHandler);
      this.changeHandler = null;
    }

  };

  HeatmapOverlay.prototype.draw = function() {
    if (!this.map) { return; }

    var bounds = this.map.getBounds();

    var topLeft = new gmaps.LatLng(
      bounds.getNorthEast().lat(),
      bounds.getSouthWest().lng()
    );

    var projection = this.getProjection();
    var point = projection.fromLatLngToDivPixel(topLeft);

    this.container.style[HeatmapOverlay.CSS_TRANSFORM] = 'translate(' +
        Math.round(point.x) + 'px,' +
        Math.round(point.y) + 'px)';

    this.update();
  };

  HeatmapOverlay.prototype.resize = function() {

    if (!this.map){ return; }

    var div = this.map.getDiv(),
      width = div.clientWidth,
      height = div.clientHeight;

    if (width == this.width && height == this.height){ return; }

    this.width = width;
    this.height = height;

    // update heatmap dimensions
    this.heatmap._renderer.setDimensions(width, height);
    // then redraw all datapoints with update
    this.update();
  };

  HeatmapOverlay.prototype.update = function() {
    var projection = this.getProjection(),
      zoom, scale, bounds, topLeft;
    var generatedData = { max: this.max, min: this.min, data: [] };

    if (!projection){ return; }

    bounds = this.map.getBounds();

    topLeft = new gmaps.LatLng(
      bounds.getNorthEast().lat(),
      bounds.getSouthWest().lng()
    );

    zoom = this.map.getZoom();
    scale = Math.pow(2, zoom);

    this.resize();

    if (this.data.length == 0) {
      if (this.heatmap) {
        this.heatmap.setData(generatedData);
      }
      return;
    }


    var latLngPoints = [];
    // iterate through data 
    var len = this.data.length;
    var layerProjection = this.getProjection();
    var layerOffset = layerProjection.fromLatLngToDivPixel(topLeft);
    var radiusMultiplier = this.cfg.scaleRadius ? scale : 1;
    var localMax = 0;
    var localMin = 0;
    var valueField = this.cfg.valueField;


    while (len--) {
      var entry = this.data[len];
      var value = entry[valueField];
      var latlng = entry.latlng;


      // we don't wanna render points that are not even on the map ;-)
      if (!bounds.contains(latlng)) {
        continue;
      }
      // local max is the maximum within current bounds
      localMax = Math.max(value, localMax);
      localMin = Math.min(value, localMin);

      var point = layerProjection.fromLatLngToDivPixel(latlng);
      var latlngPoint = { x: Math.round(point.x - layerOffset.x), y: Math.round(point.y - layerOffset.y) };
      latlngPoint[valueField] = value;

      var radius;

      if (entry.radius) {
        radius = entry.radius * radiusMultiplier;
      } else {
        radius = (this.cfg.radius || 2) * radiusMultiplier;
      }
      latlngPoint.radius = radius;
      latLngPoints.push(latlngPoint);
    }
    if (this.cfg.useLocalExtrema) {
      generatedData.max = localMax;
      generatedData.min = localMin;
    }

    generatedData.data = latLngPoints;

    this.heatmap.setData(generatedData);

  };

  HeatmapOverlay.prototype.setData = function(data) { 
    this.max = data.max;
    this.min = data.min;

    var latField = this.cfg.latField || 'lat';
    var lngField = this.cfg.lngField || 'lng';
    var valueField = this.cfg.valueField || 'value';

    // transform data to latlngs
    var data = data.data;
    var len = data.length;
    var d = [];

    while (len--) {
      var entry = data[len];
      var latlng = new gmaps.LatLng(entry[latField], entry[lngField]);
      var dataObj = { latlng: latlng };
      dataObj[valueField] = entry[valueField];
      if (entry.radius) {
        dataObj.radius = entry.radius;
      }
      d.push(dataObj);
    }
    this.data = d;
    this.update();
  };
  // experimential. not ready yet.
  HeatmapOverlay.prototype.addData = function(pointOrArray) {
    if (pointOrArray.length > 0) {
        var len = pointOrArray.length;
        while(len--) {
          this.addData(pointOrArray[len]);
        }
      } else {
        var latField = this.cfg.latField || 'lat';
        var lngField = this.cfg.lngField || 'lng';
        var valueField = this.cfg.valueField || 'value';
        var entry = pointOrArray;
        var latlng = new gmaps.LatLng(entry[latField], entry[lngField]);
        var dataObj = { latlng: latlng };
        
        dataObj[valueField] = entry[valueField];
        if (entry.radius) {
          dataObj.radius = entry.radius;
        }
        this.max = Math.max(this.max, dataObj[valueField]);
        this.min = Math.min(this.min, dataObj[valueField]);
        this.data.push(dataObj);
        this.update();
      }
  };

  return HeatmapOverlay;

});
